package com.dream.banana.dubbo.filter;

import com.alibaba.dubbo.common.extension.Activate;
import com.alibaba.dubbo.common.json.JSON;
import com.alibaba.dubbo.remoting.TimeoutException;
import com.alibaba.dubbo.rpc.Filter;
import com.alibaba.dubbo.rpc.Invocation;
import com.alibaba.dubbo.rpc.Invoker;
import com.alibaba.dubbo.rpc.Result;
import com.alibaba.dubbo.rpc.RpcContext;
import com.alibaba.dubbo.rpc.RpcException;
import java.io.IOException;
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.text.SimpleDateFormat;
import java.util.Arrays;
import java.util.Date;

import com.dream.banana.dubbo.filter.annotation.AfterLog;
import com.dream.banana.dubbo.filter.annotation.BeforLog;
import com.dream.banana.dubbo.filter.exception.BusinessException;
import com.dream.banana.dubbo.filter.pointcut.AfterPointcut;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
 * 躲了一辈子雨，雨会不会很慢过。
 * </p>
 * Created by zhumenglong on 2017/5/28.
 */
@Activate(group = { "provider" })
public class BusinessLogFilter implements Filter {
    private static final Logger logger = LoggerFactory.getLogger(BusinessLogFilter.class);
    private static final String MESSAGE_DATE_FORMAT = "yyyy-MM-dd HH:mm:ss";
    private AfterPointcut afterPointcut;

    public void setAfterPointcut(AfterPointcut afterPointcut) {
        this.afterPointcut = afterPointcut;
    }

    public Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException {
        Result result = null;
        String methodName = invocation.getMethodName();
        String accessLog = this.getAccessLog(invoker, invocation);
        if (this.isHaveAnnotation(invoker, invocation, BeforLog.class)) {
            logger.debug(accessLog);
        }

        boolean arg12 = false;

        Result ee;
        try {
            arg12 = true;
            result = invoker.invoke(invocation);
            if (result.getException() != null) {
                logger.error("方法调用异常,方法名为: {} 参数为: {}, 请求链接为 {}, 异常信息为:{}", new Object[] { invocation.getMethodName(),
                        Arrays.toString(invocation.getArguments()), invoker.getUrl(), result.getException() });
            }

            ee = result;
            arg12 = false;
        } catch (RpcException arg13) {
            if (arg13.getCause() != null && arg13.getCause() instanceof TimeoutException) {
                logger.error("方法调用超时,方法名为: {} 参数为: {}, 请求链接为 {}, 异常信息为:{}", new Object[] { invocation.getMethodName(),
                        Arrays.toString(invocation.getArguments()), invoker.getUrl(), arg13 });
            } else {
                logger.error("方法调用异常,方法名为: {} 参数为: {}, 请求链接为 {}, 异常信息为:{}", new Object[] { invocation.getMethodName(),
                        Arrays.toString(invocation.getArguments()), invoker.getUrl(), arg13 });
            }

            throw arg13;
        } catch (Exception arg14) {
            logger.error("方法调用异常,方法名为: {} 参数为: {}, 请求链接为 {}, 异常信息为:{}", new Object[] { invocation.getMethodName(),
                    Arrays.toString(invocation.getArguments()), invoker.getUrl(), arg14 });
            throw new BusinessException(arg14);
        } finally {
            if (arg12) {
                if (this.afterPointcut.isTargetMethod(methodName)
                        || this.isHaveAnnotation(invoker, invocation, AfterLog.class)) {
                    recordLog(result, accessLog);
                }

            }
        }

        if (this.afterPointcut.isTargetMethod(methodName)
                || this.isHaveAnnotation(invoker, invocation, AfterLog.class)) {
            recordLog(result, accessLog);
        }

        return ee;
    }

    private void recordLog(Result result, String accessLog) {
        StringBuilder sn1 = new StringBuilder();
        sn1.append(accessLog);
        sn1.append(" ");
        sn1.append("enforcement result -> ");
        sn1.append(result);
        accessLog = sn1.toString();
        logger.debug(accessLog);
    }

    private <T extends Annotation> boolean isHaveAnnotation(Invoker<?> invoker, Invocation invocation, Class<T> clazz) {
        String methodName = invocation.getMethodName();
        Class[] parameterTypes = invocation.getParameterTypes();

        try {
            Method e = invoker.getInterface().getMethod(methodName, parameterTypes);
            return e.getAnnotation(clazz) != null ? true : invoker.getInterface().getAnnotation(clazz) != null;
        } catch (SecurityException | NoSuchMethodException arg6) {
            logger.error("在类{}或者其继承的类中找不到公共方法{}, 异常信息为:{}",
                    new Object[] { invoker.getInterface(), methodName, arg6, arg6 });
            return false;
        }
    }

    private String getAccessLog(Invoker<?> invoker, Invocation invocation) {
        RpcContext context = RpcContext.getContext();
        String serviceName = invoker.getInterface().getName();
        String version = invoker.getUrl().getParameter("version");
        String group = invoker.getUrl().getParameter("group");
        StringBuilder sn = new StringBuilder();
        sn.append("[").append((new SimpleDateFormat("yyyy-MM-dd HH:mm:ss")).format(new Date())).append("] ")
                .append(context.getRemoteHost()).append(":").append(context.getRemotePort()).append(" -> ")
                .append(context.getLocalHost()).append(":").append(context.getLocalPort()).append(" - ");
        if (null != group && group.length() > 0) {
            sn.append(group).append("/");
        }

        sn.append(serviceName);
        if (null != version && version.length() > 0) {
            sn.append(":").append(version);
        }

        sn.append(" ");
        sn.append(invocation.getMethodName());
        sn.append("(");
        Class[] types = invocation.getParameterTypes();
        if (types != null && types.length > 0) {
            boolean args = true;
            Class[] msg = types;
            int arg10 = types.length;

            for (int arg11 = 0; arg11 < arg10; ++arg11) {
                Class type = msg[arg11];
                if (args) {
                    args = false;
                } else {
                    sn.append(",");
                }

                sn.append(type.getName());
            }
        }

        sn.append(") args (");
        Object[] arg14 = invocation.getArguments();
        if (arg14 != null && arg14.length > 0) {
            try {
                sn.append(JSON.json(arg14));
                sn.append(")");
            } catch (IOException arg13) {
                logger.error("参数：{}转Json异常, 异常信息为:{}.", new Object[] { Arrays.toString(arg14), arg13, arg13 });
            }
        }

        String arg15 = sn.toString();
        return arg15;
    }
}
